package com.naiterui.ehp.bs.b2c.product.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.bo.b2c.SkuUsageBO;
import com.naiterui.ehp.bp.bo.b2c.cms.CmsSkuBO;
import com.naiterui.ehp.bp.bo.b2c.cms.MedicineBO;
import com.naiterui.ehp.bp.bo.b2c.cms.ProductBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.support.exception.BaseExceptionCodes;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.beans.BeanUtil;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bp.utils.lang.ImageUtil;
import com.naiterui.ehp.bs.b2c.common.bo.product.SkuBO;
import com.naiterui.ehp.bs.b2c.product.entity.Medicine;
import com.naiterui.ehp.bs.b2c.product.entity.MedicineDefaultUsage;
import com.naiterui.ehp.bs.b2c.product.entity.PrescriptionPlatformProduct;
import com.naiterui.ehp.bs.b2c.product.entity.Product;
import com.naiterui.ehp.bs.b2c.product.entity.Sku;
import com.naiterui.ehp.bs.b2c.product.entity.SkuImage;
import com.naiterui.ehp.bs.b2c.product.exception.ProductBusinessException;
import com.naiterui.ehp.bs.b2c.product.exception.ProductExceptionCodes;
import com.naiterui.ehp.bs.b2c.product.repository.IMedicineDefaultUsageRepository;
import com.naiterui.ehp.bs.b2c.product.repository.IMedicineRepository;
import com.naiterui.ehp.bs.b2c.product.repository.IPrescriptionPlatformProductRepository;
import com.naiterui.ehp.bs.b2c.product.repository.IProductRepository;
import com.naiterui.ehp.bs.b2c.product.repository.ISkuImageRepository;
import com.naiterui.ehp.bs.b2c.product.repository.ISkuRepository;
import com.naiterui.ehp.bs.b2c.product.service.IInnerMedicineService;
import com.naiterui.ehp.bs.b2c.product.service.ISkuImageService;
import com.naiterui.ehp.bs.b2c.product.service.ISkuService;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class SkuServiceImpl implements ISkuService {

    @Autowired
    private ISkuRepository skuRepository;
    @Autowired
    private IProductRepository productRepository;
    @Autowired
    private ISkuImageRepository skuImageRepository;
    @Autowired
    private ISkuImageService skuImageService;
    @Autowired
    private IInnerMedicineService innerMedicineService;
    @Autowired
    private IPrescriptionPlatformProductRepository prescriptionPlatformProductRepository;
    @Autowired
    private IMedicineRepository medicineRepository;
    @Autowired
    private IMedicineDefaultUsageRepository medicineDefaultUsageRepository;

    private static final String SERIAL_NUMBER_REDIS_KEY = CommonConstant.REDIS_PRE_B2C + "product_sku_serial_number_";
    /**
     * sku文件路径
     */
    private static final String FILE_PATH_SKU = ConfigUtil.getString("file.path.sku");
    private static final Logger LOGGER = LoggerFactory.getLogger(SkuServiceImpl.class);

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<Long, SkuBO> cartSkuList(Set<Long> skuIds) {
        Map<Long, SkuBO> resultMap = new HashMap<>();
        if (CollectionUtil.isEmpty(skuIds)) {
            return resultMap;
        }

        List<Sku> skus = this.skuRepository.findAllByIdIn(skuIds);
        Set<Long> pids = skus.stream().map(Sku::getProductId).collect(Collectors.toSet());
        //查询sku相关主商品
        List<Product> products = this.productRepository.findAllByIdIn(pids);
        Map<Long, Product> productMap = products.stream().collect(Collectors.toMap(Product::getId, Function.identity()));

        //skuBO
        skus.forEach(sku -> {
            SkuBO skuBO = new SkuBO();
            BeanUtil.copyProperties(sku, skuBO);
            skuBO.setSkuId(sku.getId());
            skuBO.setFullName(sku.getName());
            skuBO.setIcon(this.skuImageService.getSkuIcon(sku.getId()));

            //product信息
            Optional.ofNullable(productMap.get(sku.getProductId()))
                .ifPresent(product -> {
                    int nmpaType = product.getNmpaType();
                    skuBO.setProductName(product.getName());
                    skuBO.setRx(Product.NMPA_TYPE_RX == nmpaType ? 1 : 0);
                });

            resultMap.put(skuBO.getSkuId(), skuBO);
        });
        return resultMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdate(String operator, CmsSkuBO cmsSku, Long thirdId) throws ProductBusinessException {
        Long productId = cmsSku.getProductId();
        Product product = this.productRepository.get(productId);
        Supplier<Sku> skuSupplier = () -> {
            Sku sku = new Sku();
            long serialNumber = RedisUtil.keyOps().incr(SERIAL_NUMBER_REDIS_KEY + productId);
            sku.setNumber(product.getNumber() + "-" + serialNumber);
            sku.setTotalSale(0L);
            sku.setCreatedAt(new Date());
            sku.setCreatedBy(operator);
            return sku;
        };

        Sku sku = Optional.ofNullable(cmsSku.getId()).map(skuId -> this.skuRepository.get(skuId)).orElseGet(skuSupplier);

        Optional.of(sku).ifPresent(skuConsumer -> {
            // 复制属性
            BeanUtils.copyProperties(cmsSku, skuConsumer, "number");
            skuConsumer.setChangedAt(new Date());
            skuConsumer.setChangedBy(operator);
        });

        if (sku.getDefaultSku() == Sku.DEFAULT_SKU_YES) {
            //将所有sku更新成不是默认sku
            this.skuRepository.updateNotDefatuleSku(sku.getProductId());
        }
        if (sku.getId() == null) {
            sku.setDefaultUsage(0);
            sku.setSafeUsage(0);
            this.skuRepository.save(sku);
        } else {
            this.skuRepository.update(sku);
        }

        Long skuId = sku.getId();

        //  获取所有上传图片的绝对路径
        List<String> images = Lists.newArrayList();
        for (String image : cmsSku.getImages()) {
            String path = image;
            //如果是上传的
            if (path.contains("temp")) {
                File srcFile = new File(path);
                String destPath = path.replace("temp", sku.getId() + "");
                File destFile = new File(destPath);
                try {
                    FileUtils.copyFile(srcFile, destFile);
                } catch (IOException e) {
                    LOGGER.error("文件复制失败 inputPath:{} outputPath:{}", srcFile, destFile, e);
                    throw new ProductBusinessException(ProductExceptionCodes.PRODUCT_SKU_UPLOAD_ERROR, e);
                }
                path = destPath;
            }
            images.add(path);
        }

        // 数据库所有存在的图片
        List<SkuImage> imageList = Lists.newArrayList();

        String mainFigurePath = images.get(0);
        imageList.add(this.buildSkuImage(skuId, SkuImage.TYPE_BIG, mainFigurePath, operator));
        String iconPath = this.generateCompressPath(mainFigurePath);
        try {
            ImageUtil.compress(mainFigurePath, iconPath, SkuImage.TYPE_ICON_SIZE);
            imageList.add(this.buildSkuImage(skuId, SkuImage.TYPE_ICON, iconPath, operator));
        } catch (FileUploadException e) {
            LOGGER.error("文件上传失败 inputPath:{} outputPath:{}", mainFigurePath, iconPath, e);
            throw new ProductBusinessException(ProductExceptionCodes.PRODUCT_SKU_UPLOAD_ERROR, e);
        }

        String compressMainFigurePath = this.generateCompressPath(mainFigurePath);
        try {
            imageList.add(this.buildSkuImage(skuId, SkuImage.TYPE_THUMB, compressMainFigurePath, operator));
            ImageUtil.compress(mainFigurePath, compressMainFigurePath, SkuImage.TYPE_THUMB_SIZE);
        } catch (FileUploadException e) {
            LOGGER.error("文件上传失败 inputPath:{} outputPath:{}", mainFigurePath, compressMainFigurePath, e);
            throw new ProductBusinessException(ProductExceptionCodes.PRODUCT_SKU_UPLOAD_ERROR, e);
        }
        images.remove(0);
        for (String image : images) {
            imageList.add(this.buildSkuImage(skuId, SkuImage.TYPE_BIG, image, operator));
            String compressPath = this.generateCompressPath(image);
            try {
                ImageUtil.compress(image, compressPath, SkuImage.TYPE_THUMB_SIZE);
                imageList.add(this.buildSkuImage(skuId, SkuImage.TYPE_THUMB, compressPath, operator));
            } catch (FileUploadException e) {
                LOGGER.error("文件上传失败 inputPath:{} outputPath:{}", image, compressPath, e);
                throw new ProductBusinessException(ProductExceptionCodes.PRODUCT_SKU_UPLOAD_ERROR, e);
            }
        }

        //删除原来的所有图
        this.skuImageRepository.deleteBySkuId(sku.getId());
        //保存现在的图片
        this.skuImageRepository.saveAll(imageList);

        product.setRelationSku(0);
        if (sku.getStatus() == Sku.STATUS_UP) {
            product.setRelationSku(1);
        } else {
            List<Sku> skus = this.skuRepository.findByProductId(sku.getProductId());
            skus.stream().filter(skus1 -> skus1.getStatus() == Sku.STATUS_UP).map(skus1 -> 1).forEachOrdered(product::setRelationSku);
        }
        product.setChangedAt(new Date());
        product.setChangedBy(operator);
        this.productRepository.update(product);

        // 维护处方流转平台商品是否添加为商品状态
        if (null != thirdId) {
            log.info("维护处方流转平台商品是否添加为商品状态 {}", thirdId);
            PrescriptionPlatformProduct prescriptionPlatformProduct = prescriptionPlatformProductRepository.get(thirdId);
            prescriptionPlatformProduct.setAddMedicineTag(PrescriptionPlatformProduct.ADD_MEDICINE_YEAH);
            prescriptionPlatformProductRepository.update(prescriptionPlatformProduct);
        }

        // 同步到药品的默认用法用量
        MedicineDefaultUsage medicineDefaultUsage = this.medicineDefaultUsageRepository.getBySkuId(skuId);
        if (medicineDefaultUsage == null) {
            medicineDefaultUsage = new MedicineDefaultUsage();
            medicineDefaultUsage.setSkuId(skuId);
            medicineDefaultUsage.setPackagSpec(cmsSku.getPackingUnitNumber());
            medicineDefaultUsage.setCreatedAt(new Date());
            medicineDefaultUsage.setCreatedBy("admin");
            medicineDefaultUsage.setChangedAt(new Date());
            medicineDefaultUsage.setChangedBy("admin");
            this.medicineDefaultUsageRepository.save(medicineDefaultUsage);
        } else {
            medicineDefaultUsage.setSkuId(skuId);
            medicineDefaultUsage.setPackagSpec(cmsSku.getPackingUnitNumber());
            medicineDefaultUsage.setChangedAt(new Date());
            medicineDefaultUsage.setChangedBy("admin");
            this.medicineDefaultUsageRepository.update(medicineDefaultUsage);
        }
    }

    private SkuImage buildSkuImage(Long skuId, Integer type, String path, String operator) {
        SkuImage skuImage = new SkuImage();
        skuImage.setSkuId(skuId);
        skuImage.setType(type);
        skuImage.setPath(path.replace(FILE_PATH_SKU, ""));
        skuImage.setCreatedAt(new Date());
        skuImage.setCreatedBy(operator);
        skuImage.setChangedAt(new Date());
        skuImage.setChangedBy(operator);
        return skuImage;
    }

    private String generateCompressPath(String path) {
        //生成新的文件名
        File file = new File(path);
        String name = file.getName();
        return file.getParent() + "/" + UUID.randomUUID() + "." + name.split("\\.")[1];
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDefault(Long productId, Long skuId, String operator) {
        Optional<Sku> skuOptional = this.skuRepository.findById(skuId);
        Consumer<Sku> skuConsumer = sku -> {
            //将所有sku更新成不是默认sku
            this.skuRepository.updateNotDefatuleSku(productId);
            sku.setDefaultSku(Sku.DEFAULT_SKU_YES);
            sku.setChangedAt(new Date());
            sku.setChangedBy(operator);
            this.skuRepository.update(sku);
        };
        skuOptional.ifPresent(skuConsumer);

    }

    @Override
    public List<SkuBO> getListBySkuIds(Set<Long> skuIds) {

        if (CollectionUtil.isEmpty(skuIds)) {
            return Lists.newArrayList();
        }

        List<Sku> list = this.skuRepository.findAllByIdIn(skuIds);
        Set<Long> pids = list.stream().map(Sku::getProductId).collect(Collectors.toSet());
        List<Product> products = this.productRepository.findAllByIdIn(pids);
        Map<Long, Product> productMap = products.stream().collect(Collectors.toMap(Product::getId, Function.identity()));

        return list.stream()
            .map(sku -> {
                Product product = productMap.get(sku.getProductId());
                SkuBO skuBO = new SkuBO();
                skuBO.setProductId(sku.getProductId());
                skuBO.setProductName(product.getName());
                skuBO.setSkuId(sku.getId());
                skuBO.setNumber(sku.getNumber());
                skuBO.setPharmacologyClassificationId(product.getClassificationId());
                skuBO.setFullName(sku.getName());
                skuBO.setRx(Product.NMPA_TYPE_RX == product.getNmpaType() ? 1 : 0);
                skuBO.setSalePrice(sku.getSalePrice());
                skuBO.setStatus(sku.getStatus());
                return skuBO;
            }).collect(Collectors.toList());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateProduct(String operator, ProductBO product) throws ProductBusinessException {
        log.info("保存商品信息，参数为 {}", JsonMapper.toJson(product));
        MedicineBO medicine = product.getMedicine();
        Long productId = this.innerMedicineService.saveOrUpdate(operator, medicine);
        CmsSkuBO skuBo = product.getSku();
        skuBo.setName(skuBo.getName());
        skuBo.setDefaultSku(Sku.DEFAULT_SKU_YES);
        skuBo.setProductId(productId);
        this.saveOrUpdate(operator, skuBo, medicine.getThirdId());
    }

    @Override
    public Map<Long, Integer> getSkuPrice(Set<Long> skuIds) {
        List<SkuBO> skuBOS = this.getListBySkuIds(skuIds);
        return skuBOS.stream().collect(Collectors.toMap(SkuBO::getSkuId, SkuBO::getSalePrice, (a, b) -> b));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSkuUsage(SkuUsageBO skuUsageBO) throws BusinessException {
        log.info("修改sku用法用量和安全用量: {}", JsonMapper.toJson(skuUsageBO));
        Sku sku = this.skuRepository.get(skuUsageBO.getId());
        if (sku == null) {
            throw new BusinessException(BaseExceptionCodes.PARAM_ERROR);
        }
        Integer defaultSku = skuUsageBO.getDefaultUsage();
        if (defaultSku != null) {
            sku.setDefaultUsage(defaultSku);
        }
        Integer safeUsage = skuUsageBO.getSafeUsage();
        if (safeUsage != null) {
            sku.setSafeUsage(safeUsage);
        }
        Integer packagSpec = skuUsageBO.getPackagSpec();
        if (packagSpec != null) {
            sku.setPackingUnitNumber(packagSpec);
        }
        sku.setLastConfigTime(skuUsageBO.getLastConfigTime());
        this.skuRepository.update(sku);
        String usageDosage = skuUsageBO.getUsageDosage();
        String quantityUnit = skuUsageBO.getQuantityUnit();
        if (StrUtil.isNotBlank(quantityUnit) || StrUtil.isNotBlank(usageDosage)) {
            Medicine medicine = this.medicineRepository.findByProductId(sku.getProductId());
            if (StrUtil.isNotBlank(quantityUnit)) {
                medicine.setPackingUnit(quantityUnit);
            }
            if (StrUtil.isNotBlank(usageDosage)) {
                medicine.setUsageDosage(usageDosage);
            }
            this.medicineRepository.update(medicine);
        }
    }
}
